home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 3: CDPD 3 / Almathera Ten on Ten - Disc 3: CDPD3.iso / fish / 001-100 / 001-025 / 022 / lemacs / main.c < prev    next >
C/C++ Source or Header  |  1995-03-17  |  22KB  |  685 lines

  1. /*
  2.  * This program is in public domain; written by Dave G. Conroy.
  3.  * This file contains the main driving routine, and some keyboard processing
  4.  * code, for the MicroEMACS screen editor.
  5.  *
  6.  * REVISION HISTORY:
  7.  *
  8.  * 1.0  Steve Wilhite, 30-Nov-85
  9.  *      - Removed the old LK201 and VT100 logic. Added code to support the
  10.  *        DEC Rainbow keyboard (which is a LK201 layout) using the the Level
  11.  *        1 Console In ROM INT. See "rainbow.h" for the function key defs
  12.  *      Steve Wilhite, 1-Dec-85
  13.  *      - massive cleanup on code in display.c and search.c
  14.  *
  15.  * 2.0  George Jones, 12-Dec-85
  16.  *      - Ported to Amiga.
  17.  *
  18.  * 3.0  Daniel Lawrence, 29-Dec-85
  19.  *      - rebound keys/added new fast buffered I/O for AMIGA
  20.  *    - added META- repeat commands
  21.  *    - added reposition default to center screen (yeah!)
  22.  *    - changed exit with modified buffers message
  23.  *    - made filesave tell us what it is doing
  24.  *    - changed search string entry to terminate with <ESC>
  25.  *      so we can use <NL> in search/replace strings
  26.  *    - updated version number in mode line to 3.0
  27.  *    12-Jan-86
  28.  *    - Added code to reconize the Search/replace functions
  29.  *    - Added code to perform search/replace & query functions
  30.  *    14-Jan-86
  31.  *    - moved search logic to separate function in search.c
  32.  *    - added replace and query replace functions
  33.  *    - separated out control key expansions to be used by others in search.c
  34.  *    15-Jan-86
  35.  *    - changed "visiting" to finding
  36.  *    - changed yes/no responces to not need return
  37.  *    - cleaned up various messages
  38.  *    16-jan-86
  39.  *    - fixed spurious spawn message in MSDOS
  40.  *    - added ^X-S synonime to save command
  41.  *    - moved escape to shell to ^X-C
  42.  *    21-jan-86
  43.  *    - added code to suspend shell under BSD
  44.  *    22-jan-86
  45.  *    - added function key support (SPEC) under MSDOS
  46.  *    - Abort now prints [Aborted] on message line
  47.  *    23-jan-86
  48.  *    - Added modes and commends to set/unset them
  49.  *    24-jan-86
  50.  *    - Added Goto Line command
  51.  *    - added Rename Buffer command
  52.  *    28-jan-86
  53.  *    - added goto begining and end of paragraph commands (META-P/META-N)
  54.  *    - re-wrote kdelete to use realloc. gained MUCH speed here when
  55.  *      doing large wipes both on UNIX and MSDOS. Changed kill buffer
  56.  *      allocation block size from 256 bytes to 1 k
  57.  *    29-jan-86
  58.  *    - moved extern function declarations to efunc.h
  59.  *    - made name[] name binding table
  60.  *    30-jan-86
  61.  *    - fixed Previous/Next paragraph command not to wrap around EOF
  62.  *    - added Fill Paragraph command (META-Q)
  63.  *    4-feb-86
  64.  *    - added code to properly display long lines, scrolling them right
  65.  *      to left
  66.  *    5-feb-85
  67.  *    - rewrote code to right/left scroll...much better
  68.  *    - added shifted arror keys on IBMPC
  69.  *    6-feb-85
  70.  *    - add option to allow forword-word to jump to begining of
  71.  *      next word instead of end of current one. This is different from
  72.  *      other emacs' but can be configured off in estruct.h
  73.  *    - added VIEW mode to allow a buffer to be read only
  74.  *       (-v switch on command line will activate this)
  75.  *    - changed quick exit to write out ALL changed buffers!!!
  76.  *      MAKE SURE YOU KNOW THIS WHEN META-Zing
  77.  *    10-feb-86
  78.  *    - added handling of lines longer than allowed on file read in
  79.  *      (they wrap on additional lines)
  80.  *    - made having space clear the message line and NOT insert itself
  81.  *      a configuration option in ed.h
  82.  *    11-feb-86
  83.  *    - added Describe-command and Help commands.
  84.  *    13-feb-86
  85.  *    - added View file command (^X ^V) and finished HELP command
  86.  *    14-feb-86
  87.  *    - added option to let main loop skip update if type ahead commands
  88.  *       are queued up
  89.  *    16-feb-86
  90.  *    - added Insert File command
  91.  *    17-feb-86
  92.  *    - added scroll next window up/down commands
  93.  *    18-feb-86
  94.  *    - added CMODE indentation
  95.  *    - re-arranged header files to standerdize extern and global
  96.  *      definitions
  97.  *    - changed version number to 3.2
  98.  *    - added numeric arguments to search, reverse search and
  99.  *      search and replace
  100.  *    24-feb-86
  101.  *    - added Bind To Key function (^C for now) to allow the user
  102.  *      to change his command keys
  103.  *    - added Unbind key function (M-^C for now)
  104.  *    - added execute named command to execute unbound commands (M-X)
  105.  *    - added describe bindings command (not bound)
  106.  *    - changed version number to 3.3
  107.  *    25-feb-86
  108.  *    - scrapped CERROR mode (too many compilers)
  109.  *    - added EXACT mode for case sensitive searchers
  110.  *    26-feb-86
  111.  *    - added command completion on execute named command and
  112.  *      all routined grabbing a command name
  113.  *    - adding execute-command-line command and its support functions
  114.  *      (in preporation for sourcing files)
  115.  *    - added Execute Buffer command
  116.  *    27-feb-86
  117.  *    - added execute(source) file command and added code to automatically
  118.  *      execute emacs.rc (or .emacsrc on UNIX) before initial read in
  119.  *    - changed version number to 3.4
  120.  *    4-mar-86
  121.  *    - changed word delete to be consistant with word move (it gets
  122.  *      rid of the inter word space now) This is configurable with the
  123.  *      NFWORD symbol in estruct.h
  124.  *    - added B_ACTIVE entry to the buffer table. Let emacs read multiple
  125.  *      file names from the command line and only read them in as needed
  126.  *    5-mar-85
  127.  *    - rewrote command line parser to get rid of my patchy code
  128.  *    - changed version number to 3.5
  129.  *    1-apr-86
  130.  *    - added support for Aztec C 3.20e under MSDOS
  131.  *    - fixed bug in mlwrite on ADM3's and thier ilk under V7
  132.  *    - added insertion of pounds in column one under CMODE
  133.  *    - changed version number to 3.6
  134.  *    3-apr-86
  135.  *    - added next-buffer command (^X-X)
  136.  *    5-apr-86
  137.  *    - added kill paragraph command (M-^W)
  138.  *    - changed fill-paragraph to leave 2 spaces after a period at the
  139.  *      end of a word.
  140.  *    - added OVERWRITE mode
  141.  *    7-apr-86
  142.  *    - fixed overwrite mode to handle tabs
  143.  *    8-apr-86
  144.  *    - added add/delete global mode (<ESC>M & <ESC> ^M) commands
  145.  *    9-apr-86
  146.  *    - added insert space command
  147.  *    - moved bindings around        ^C    insert space
  148.  *                    M-K    bind-to-key
  149.  *                    INSERT    insert space
  150.  *                    DELETE    forwdel
  151.  *    - added hunt forward and hunt reverse commands
  152.  *    10-apr-86
  153.  *    - fixed bug in DOBUF with non-terminated command string
  154.  *    15-apr-86
  155.  *    - fixed tab expansion bug in DISPLAY which hung the AMIGA
  156.  *      (send in by Dawn Banks)
  157.  *    - fixed curcol problen if forwline/backline during keyboard
  158.  *      macro execution (sent in by Ernst Christen)
  159.  *    - added AMIGA function/cursor key support
  160.  *    - fixed nonterminating <NL> replacement bug
  161.  *    - fixed word wrapping problems
  162.  *    16-apr-86
  163.  *    - updated documentation and froze development for 3.6 net release
  164.  */
  165.  
  166. #include        <stdio.h>
  167.  
  168. /* make global definitions not external */
  169. #define    maindef
  170.  
  171. #include        "estruct.h"    /* global structures and defines */
  172. #include    "efunc.h"    /* function declarations and name table    */
  173. #include    "edef.h"    /* global definitions */
  174. #include    "ebind.h"    /* default key bindings */
  175.  
  176. #if     VMS
  177. #include        <ssdef.h>
  178. #define GOOD    (SS$_NORMAL)
  179. #endif
  180.  
  181. #ifndef GOOD
  182. #define GOOD    0
  183. #endif
  184.  
  185. main(argc, argv)
  186. char    *argv[];
  187. {
  188.         register int    c;
  189.         register int    f;
  190.         register int    n;
  191.         register int    mflag;
  192.     register BUFFER *bp;
  193.     register int    ffile;        /* first file flag */
  194.     register int    carg;        /* current arg to scan */
  195.     int basec;            /* c stripped of meta character */
  196.     int viewflag;            /* are we starting in view mode? */
  197.         char bname[NBUFN];        /* buffer name of file to read */
  198.  
  199.     /* initialize the editor and process the startup file */
  200.         strcpy(bname, "main");    /* default buffer name */
  201.         edinit(bname);        /* Buffers, windows.    */
  202.         vtinit();        /* Displays.            */
  203.     startup();        /* execute .emacsrc if there */
  204.     viewflag = FALSE;
  205.     ffile = TRUE;        /* no file to edit yet */
  206.     update();        /* let the user know we are here */
  207.     
  208.     /* scan through the command line and get the files to edit */
  209.     for (carg = 1; carg < argc; ++carg) {
  210.         /* if its a switch, process it */
  211.         if (argv[carg][0] == '-') {
  212.             switch (argv[carg][1]) {
  213.                 case 'v':    /* -v for View File */
  214.                 case 'V':
  215.                     viewflag = TRUE;
  216.                     break;
  217.                 case 'e':    /* -e for Edit file */
  218.                 case 'E':
  219.                     viewflag = FALSE;
  220.                     break;
  221.                 default:    /* unknown switch */
  222.                     /* ignore this for now */
  223.                     break;
  224.             }
  225.         } else {    /* process a file name */
  226.             /* set up a buffer for this file */
  227.                     makename(bname, argv[carg]);
  228.  
  229.             /* if this is the first file, read it in */
  230.             if (ffile) {
  231.                 bp = curbp;
  232.                 makename(bname, argv[carg]);
  233.                 strcpy(bp->b_bname, bname);
  234.                 strcpy(bp->b_fname, argv[carg]);
  235.                 if (readin(argv[carg], (viewflag==FALSE))
  236.                                 == ABORT) {
  237.                     strcpy(bp->b_bname, "main");
  238.                     strcpy(bp->b_fname, "");
  239.                 }
  240.                 bp->b_dotp = bp->b_linep;
  241.                 bp->b_doto = 0;
  242.                 ffile = FALSE;
  243.             } else {
  244.                 /* set this to inactive */
  245.                 bp = bfind(bname, TRUE, 0);
  246.                 strcpy(bp->b_fname, argv[carg]);
  247.                 bp->b_active = FALSE;
  248.             }
  249.  
  250.             /* set the view mode appropriatly */
  251.             if (viewflag)
  252.                 bp->b_mode |= MDVIEW;
  253.         }
  254.     }
  255.  
  256.     /* setup to process commands */
  257.         lastflag = 0;                           /* Fake last flags.     */
  258.     curbp->b_mode = curbp->b_mode | gmode;    /* and set default modes*/
  259.     curwp->w_flag |= WFMODE;        /* and force an update    */
  260.  
  261. loop:
  262.         update();                               /* Fix up the screen    */
  263.         c = getkey();
  264.         if (mpresf != FALSE) {
  265.                 mlerase();
  266.                 update();
  267. #if    CLRMSG
  268.                 if (c == ' ')                   /* ITS EMACS does this  */
  269.                         goto loop;
  270. #endif
  271.         }
  272.         f = FALSE;
  273.         n = 1;
  274.  
  275.     /* do META-# processing if needed */
  276.  
  277.     basec = c & ~META;        /* strip meta char off if there */
  278.     if ((c & META) && ((basec >= '0' && basec <= '9') || basec == '-')) {
  279.         f = TRUE;        /* there is a # arg */
  280.         n = 0;            /* start with a zero default */
  281.         mflag = 1;        /* current minus flag */
  282.         c = basec;        /* strip the META */
  283.         while ((c >= '0' && c <= '9') || (c == '-')) {
  284.             if (c == '-') {
  285.                 /* already hit a minus or digit? */
  286.                 if ((mflag == -1) || (n != 0))
  287.                     break;
  288.                 mflag = -1;
  289.             } else {
  290.                 n = n * 10 + (c - '0');
  291.             }
  292.             if ((n == 0) && (mflag == -1))    /* lonely - */
  293.                 mlwrite("Arg:");
  294.             else
  295.                 mlwrite("Arg: %d",n * mflag);
  296.  
  297.             c = getkey();    /* get the next key */
  298.         }
  299.         n = n * mflag;    /* figure in the sign */
  300.     }
  301.  
  302.     /* do ^U repeat argument processing */
  303.  
  304.         if (c == (CTRL|'U')) {                  /* ^U, start argument   */
  305.                 f = TRUE;
  306.                 n = 4;                          /* with argument of 4 */
  307.                 mflag = 0;                      /* that can be discarded. */
  308.                 mlwrite("Arg: 4");
  309.                 while ((c=getkey()) >='0' && c<='9' || c==(CTRL|'U') || c=='-'){
  310.                         if (c == (CTRL|'U'))
  311.                                 n = n*4;
  312.                         /*
  313.                          * If dash, and start of argument string, set arg.
  314.                          * to -1.  Otherwise, insert it.
  315.                          */
  316.                         else if (c == '-') {
  317.                                 if (mflag)
  318.                                         break;
  319.                                 n = 0;
  320.                                 mflag = -1;
  321.                         }
  322.                         /*
  323.                          * If first digit entered, replace previous argument
  324.                          * with digit and set sign.  Otherwise, append to arg.
  325.                          */
  326.                         else {
  327.                                 if (!mflag) {
  328.                                         n = 0;
  329.                                         mflag = 1;
  330.                                 }
  331.                                 n = 10*n + c - '0';
  332.                         }
  333.                         mlwrite("Arg: %d", (mflag >=0) ? n : (n ? -n : -1));
  334.                 }
  335.                 /*
  336.                  * Make arguments preceded by a minus sign negative and change
  337.                  * the special argument "^U -" to an effective "^U -1".
  338.                  */
  339.                 if (mflag == -1) {
  340.                         if (n == 0)
  341.                                 n++;
  342.                         n = -n;
  343.                 }
  344.         }
  345.         if (c == (CTRL|'X'))                    /* ^X is a prefix       */
  346.                 c = CTLX | getctl();
  347.         if (kbdmip != NULL) {                   /* Save macro strokes.  */
  348.                 if (c!=(CTLX|')') && kbdmip>&kbdm[NKBDM-6]) {
  349.                         ctrlg(FALSE, 0);
  350.                         goto loop;
  351.                 }
  352.                 if (f != FALSE) {
  353.                         *kbdmip++ = (CTRL|'U');
  354.                         *kbdmip++ = n;
  355.                 }
  356.                 *kbdmip++ = c;
  357.         }
  358.         execute(c, f, n);                       /* Do it.               */
  359.         goto loop;
  360. }
  361.  
  362. /*
  363.  * Initialize all of the buffers and windows. The buffer name is passed down
  364.  * as an argument, because the main routine may have been told to read in a
  365.  * file by default, and we want the buffer name to be right.
  366.  */
  367. edinit(bname)
  368. char    bname[];
  369. {
  370.         register BUFFER *bp;
  371.         register WINDOW *wp;
  372.     char *malloc();
  373.  
  374.         bp = bfind(bname, TRUE, 0);             /* First buffer         */
  375.         blistp = bfind("[List]", TRUE, BFTEMP); /* Buffer list buffer   */
  376.         wp = (WINDOW *) malloc(sizeof(WINDOW)); /* First window         */
  377.         if (bp==NULL || wp==NULL || blistp==NULL)
  378.                 exit(1);
  379.         curbp  = bp;                            /* Make this current    */
  380.         wheadp = wp;
  381.         curwp  = wp;
  382.         wp->w_wndp  = NULL;                     /* Initialize window    */
  383.         wp->w_bufp  = bp;
  384.         bp->b_nwnd  = 1;                        /* Displayed.           */
  385.         wp->w_linep = bp->b_linep;
  386.         wp->w_dotp  = bp->b_linep;
  387.         wp->w_doto  = 0;
  388.         wp->w_markp = NULL;
  389.         wp->w_marko = 0;
  390.         wp->w_toprow = 0;
  391.         wp->w_ntrows = term.t_nrow-1;           /* "-1" for mode line.  */
  392.         wp->w_force = 0;
  393.         wp->w_flag  = WFMODE|WFHARD;            /* Full.                */
  394. }
  395.  
  396. /*
  397.  * This is the general command execution routine. It handles the fake binding
  398.  * of all the keys to "self-insert". It also clears out the "thisflag" word,
  399.  * and arranges to move it to the "lastflag", so that the next command can
  400.  * look at it. Return the status of command.
  401.  */
  402. execute(c, f, n)
  403. {
  404.         register KEYTAB *ktp;
  405.         register int    status;
  406.  
  407.         ktp = &keytab[0];                       /* Look in key table.   */
  408.         while (ktp->k_fp != NULL) {
  409.                 if (ktp->k_code == c) {
  410.                         thisflag = 0;
  411.                         status   = (*ktp->k_fp)(f, n);
  412.                         lastflag = thisflag;
  413.                         return (status);
  414.                 }
  415.                 ++ktp;
  416.         }
  417.  
  418.         /*
  419.          * If a space was typed, fill column is defined, the argument is non-
  420.          * negative, wrap mode is enabled, and we are now past fill column,
  421.      * and we are not read-only, perform word wrap.
  422.          */
  423.         if (c == ' ' && (curwp->w_bufp->b_mode & MDWRAP) && fillcol > 0 &&
  424.         n >= 0 && getccol(FALSE) > fillcol &&
  425.         (curwp->w_bufp->b_mode & MDVIEW) == FALSE)
  426.                 wrapword();
  427.  
  428.         if ((c>=0x20 && c<=0x7E)                /* Self inserting.      */
  429.         ||  (c>=0xA0 && c<=0xFE)) {
  430.                 if (n <= 0) {                   /* Fenceposts.          */
  431.                         lastflag = 0;
  432.                         return (n<0 ? FALSE : TRUE);
  433.                 }
  434.                 thisflag = 0;                   /* For the future.      */
  435.  
  436.         /* if we are in overwrite mode, not at eol,
  437.            and next char is not a tab or we are at a tab stop,
  438.            delete a char forword            */
  439.         if (curwp->w_bufp->b_mode & MDOVER &&
  440.             curwp->w_doto < curwp->w_dotp->l_used &&
  441.             (lgetc(curwp->w_dotp, curwp->w_doto) != '\t' ||
  442.              (curwp->w_doto) % 8 == 7))
  443.                 ldelete(1, FALSE);
  444.  
  445.         /* do the appropriate insertion */
  446.         if (c == '}' && (curbp->b_mode & MDCMOD) != 0)
  447.                 status = insbrace(n, c);
  448.             else if (c == '#' && (curbp->b_mode & MDCMOD) != 0)
  449.                 status = inspound();
  450.             else
  451.                     status = linsert(n, c);
  452.  
  453.                 lastflag = thisflag;
  454.                 return (status);
  455.         }
  456.     mlwrite("\007[Key not bound]");        /* complain        */
  457.         lastflag = 0;                           /* Fake last flags.     */
  458.         return (FALSE);
  459. }
  460.  
  461. /*
  462.  * Read in a key.
  463.  * Do the standard keyboard preprocessing. Convert the keys to the internal
  464.  * character set.
  465.  */
  466. getkey()
  467. {
  468.     int    c;
  469. #if    AMIGA
  470.     int    d;
  471. #endif
  472.  
  473.         c = (*term.t_getchar)();
  474.  
  475. #if RAINBOW
  476.  
  477.         if (c & Function_Key)
  478.                 {
  479.                 int i;
  480.  
  481.                 for (i = 0; i < lk_map_size; i++)
  482.                         if (c == lk_map[i][0])
  483.                                 return lk_map[i][1];
  484.                 }
  485.         else if (c == Shift + 015) return CTRL | 'J';
  486.         else if (c == Shift + 0x7F) return META | 0x7F;
  487. #endif
  488.  
  489. #if    MSDOS
  490.     if (c == 0) {                /* Apply SPEC prefix    */
  491.         c = getkey();
  492.         return(SPEC | c);
  493.     }
  494. #endif
  495.  
  496. #if    AMIGA
  497.     /* apply SPEC prefix */
  498.     if ((unsigned)c == 155) {
  499.         c = (*term.t_getchar)();
  500.  
  501.         /* first try to see if it is a cursor key */
  502.         if ((c >= 'A' && c <= 'D') || c == 'S' || c == 'T')
  503.             return(SPEC | c);
  504.  
  505.         /* next, a 2 char sequence */
  506.         d = (*term.t_getchar)();
  507.         if (d == '~')
  508.             return(SPEC | c);
  509.  
  510.         /* decode a 3 char sequence */
  511.         c = d + 32;
  512.         /* if a shifted function key, eat the tilde */
  513.         if (d >= '0' && d <= '9')
  514.             d = (*term.t_getchar)();
  515.         return(SPEC | c);
  516.     }
  517. #endif
  518.  
  519.         if (c == METACH) {                      /* Apply M- prefix      */
  520.                 c = getctl();
  521.                 return (META | c);
  522.         }
  523.  
  524.         if (c>=0x00 && c<=0x1F)                 /* C0 control -> C-     */
  525.                 c = CTRL | (c+'@');
  526.         return (c);
  527. }
  528.  
  529. /*
  530.  * Get a key.
  531.  * Apply control modifications to the read key.
  532.  */
  533. getctl()
  534. {
  535.         register int    c;
  536.  
  537.         c = (*term.t_getchar)();
  538.         if (c>='a' && c<='z')                   /* Force to upper       */
  539.                 c -= 0x20;
  540.         if (c>=0x00 && c<=0x1F)                 /* C0 control -> C-     */
  541.                 c = CTRL | (c+'@');
  542.         return (c);
  543. }
  544.  
  545. /*
  546.  * Fancy quit command, as implemented by Norm. If the any buffer has
  547.  * changed do a write on that buffer and exit emacs, otherwise simply exit.
  548.  */
  549. quickexit(f, n)
  550. {
  551.     register BUFFER *bp;    /* scanning pointer to buffers */
  552.  
  553.     bp = bheadp;
  554.     while (bp != NULL) {
  555.             if ((bp->b_flag&BFCHG) != 0    /* Changed.             */
  556.             && (bp->b_flag&BFTEMP) == 0) {    /* Real.                */
  557.             curbp = bp;        /* make that buffer cur    */
  558.             mlwrite("[Saving %s]",bp->b_fname);
  559.                     filesave(f, n);
  560.         }
  561.     bp = bp->b_bufp;            /* on to the next buffer */
  562.     }
  563.         quit(f, n);                             /* conditionally quit   */
  564. }
  565.  
  566. /*
  567.  * Quit command. If an argument, always quit. Otherwise confirm if a buffer
  568.  * has been changed and not written out. Normally bound to "C-X C-C".
  569.  */
  570. quit(f, n)
  571. {
  572.         register int    s;
  573.  
  574.         if (f != FALSE                          /* Argument forces it.  */
  575.         || anycb() == FALSE                     /* All buffers clean.   */
  576.                         /* User says it's OK.   */
  577.         || (s=mlyesno("Modified buffers exist. Leave anyway")) == TRUE) {
  578. #if    FILOCK
  579.         if (lockrel() != TRUE) {
  580.             (*term.t_putchar)('\n');
  581.             (*term.t_putchar)('\r');
  582.             (*term.t_close)();
  583.             exit(1);
  584.         }
  585. #endif
  586.                 vttidy();
  587.                 exit(GOOD);
  588.         }
  589.     mlwrite("");
  590.         return (s);
  591. }
  592.  
  593. /*
  594.  * Begin a keyboard macro.
  595.  * Error if not at the top level in keyboard processing. Set up variables and
  596.  * return.
  597.  */
  598. ctlxlp(f, n)
  599. {
  600.         if (kbdmip!=NULL || kbdmop!=NULL) {
  601.                 mlwrite("Not now");
  602.                 return (FALSE);
  603.         }
  604.         mlwrite("[Start macro]");
  605.         kbdmip = &kbdm[0];
  606.         return (TRUE);
  607. }
  608.  
  609. /*
  610.  * End keyboard macro. Check for the same limit conditions as the above
  611.  * routine. Set up the variables and return to the caller.
  612.  */
  613. ctlxrp(f, n)
  614. {
  615.         if (kbdmip == NULL) {
  616.                 mlwrite("Not now");
  617.                 return (FALSE);
  618.         }
  619.         mlwrite("[End macro]");
  620.         kbdmip = NULL;
  621.         return (TRUE);
  622. }
  623.  
  624. /*
  625.  * Execute a macro.
  626.  * The command argument is the number of times to loop. Quit as soon as a
  627.  * command gets an error. Return TRUE if all ok, else FALSE.
  628.  */
  629. ctlxe(f, n)
  630. {
  631.         register int    c;
  632.         register int    af;
  633.         register int    an;
  634.         register int    s;
  635.  
  636.         if (kbdmip!=NULL || kbdmop!=NULL) {
  637.                 mlwrite("Not now");
  638.                 return (FALSE);
  639.         }
  640.         if (n <= 0)
  641.                 return (TRUE);
  642.         do {
  643.                 kbdmop = &kbdm[0];
  644.                 do {
  645.                         af = FALSE;
  646.                         an = 1;
  647.                         if ((c = *kbdmop++) == (CTRL|'U')) {
  648.                                 af = TRUE;
  649.                                 an = *kbdmop++;
  650.                                 c  = *kbdmop++;
  651.                         }
  652.                         s = TRUE;
  653.                 } while (c!=(CTLX|')') && (s=execute(c, af, an))==TRUE);
  654.                 kbdmop = NULL;
  655.         } while (s==TRUE && --n);
  656.         return (s);
  657. }
  658.  
  659. /*
  660.  * Abort.
  661.  * Beep the beeper. Kill off any keyboard macro, etc., that is in progress.
  662.  * Sometimes called as a routine, to do general aborting of stuff.
  663.  */
  664. ctrlg(f, n)
  665. {
  666.         (*term.t_beep)();
  667.         if (kbdmip != NULL) {
  668.                 kbdm[0] = (CTLX|')');
  669.                 kbdmip  = NULL;
  670.         }
  671.     mlwrite("[Aborted]");
  672.         return (ABORT);
  673. }
  674.  
  675. /* tell the user that this command is illegal while we are in
  676.    VIEW (read-only) mode                */
  677.  
  678. rdonly()
  679.  
  680. {
  681.     (*term.t_beep)();
  682.     mlwrite("[Key illegal in VIEW mode]");
  683.     return(FALSE);
  684. }
  685.